import thread, time, logging, socket ,traceback, struct, types
from vdsAgentUtils import initLogging

class messageType:
    powerUp = 1
    powerDown = 2
    heartbeat = 3
    machineName = 4
    guestOs = 5
    IPAddresses = 6
    lastSessionMessage = 7
    userInfo = 8
    newApp = 9
    flushApps = 10
    clientIp = 11
    sessionLock = 12
    sessionUnlock = 13
    sessionLogoff = 14
    sessionLogon = 15
    agentCmd = 16


class HypercallBase:
    def hcInit(self):
        return 0

    def hcTerm(self):
        return 0

    def buildHcMsg(self, msg_type, msg):
        msg_str_len = 0
        if msg != '' and msg != None and type(msg) == types.StringType:
            msg_str_len = len(msg)
        msgLength = 16 + msg_str_len
        message = ''
        message = struct.pack("IIII",                #format of 4 integers
                          socket.htonl(1),           #channel number (equivalent to HypercallLib.HC_BALLOON)
                          socket.htonl(3),           #Hypercall action 1=register, 2=unregister, 3=forward
                          socket.htonl(msgLength),   #total message length
                          socket.htonl(msg_type)     #vdsm message type see commented class guestMType above
                          )
        if msg_str_len > 0:
            parseString = "%ds"%(len(msg))
            message += struct.pack(parseString, msg[0:])
        return message, msgLength

    def unpackMsg(self, msg):
        retval=[]
        logging.debug("HypercallBase:: unpackMsg() msg = '%s'"%(repr(msg)))
        try:
            tmp = struct.unpack("III",msg[:12])
            for val in tmp:
                retval.append(socket.ntohl(int(val)))
            cmdLen = retval[2] - 12 #length of command
            if (msg[len(msg)-1] == '\0'): #remove terminating null
                cmdLen = cmdLen -1
            logging.debug("cmdlen = %d , msg = %s"%(cmdLen, msg[12:12+cmdLen]))
            tmpCmd = struct.unpack("%ds"%(cmdLen), msg[12:12+cmdLen])
            retval.append(tmpCmd[0])
        except:
            logging.error("HypercallBase::unpackMsg - '%s'"%(traceback.format_exc()))
            retval = []
        return retval

    def hcSend(self,msg_type, msg) :
        streamed_msg, m_len = self.buildHcMsg(msg_type, msg)
        logging.debug("HypercallBase::hcSend - len = '%d'"%(m_len))
        return 0

    def hcRecv(self):
        return 0


class DataRetriverBase:
    def getMachineName(self):
        pass
    def getOsVersion(self):
        pass
    def getAllNetworkInterfaces(self):
        pass
    def getApplications(self):
        pass
    def getAvailableRAM(self):
        pass
    def getGeneralInfo(self):
        pass
    def getUsers(self):
        pass
    def getActiveUser(self):
        pass



class AgentLogicBase:
    def __init__(self):
        initLogging(self.log_file_name)
        logging.debug("AgentLogicBase:: __init__() entered")
        self.hc_drv_init = False
        self.cont_flag = True
        self.heartBitRate = 5
        self.userCheckRate = 10
        self.appRefreshRate = 120
        self.activeUser = ""
        self.hc = None
        self.dr = None
        self.commandHandler = None


    def baseRun(self):
        logging.debug("AgentLogicBase:: baseRun() entered")
        if self.hc.hcInit():
            errMsg = "AgentLogicBase::baseRun() - Failed initializing the HypercallLib, check the Hypercall driver installation"
            logging.error(errMsg)
            raise errMsg
        else:
            self.hc_drv_init == True
        thread.start_new_thread(self.doListen, ())
        thread.start_new_thread(self.doWork, ())


    def baseStop(self):
        logging.debug("AgentLogicBase:: baseStop() entered")
        self.hc.hcSend(messageType.powerDown, "")
        if self.hc.hcTerm() != 0:
            logging.error("AgentLogicBase::baseStop() - Failed terminating the HypercallLib")
        self.hc_drv_init == False
        self.cont_flag = False

    def doWork(self):
        logging.debug("AgentLogicBase:: doWork() entered")
        self.hc.hcSend(messageType.powerUp, "")
        self.sendInfo()
        self.sendUserInfo()
        self.sendAppList()
        counter = 0
        hbsecs = self.heartBitRate
        appsecs = self.appRefreshRate
        usersecs = self.userCheckRate

        try:
            while self.cont_flag:
                msg = "AgentLogicBase:: doWork() - interval = %d"%(counter)
                #logging.debug(msg)
                counter +=1
                hbsecs -= 1
                if hbsecs <= 0:
                    self.hc.hcSend(messageType.heartbeat,  self.dr.getAvailableRAM())
                    hbsecs = self.heartBitRate
                usersecs -=1
                if usersecs <=0:
                    self.sendUserInfo()
                    usersecs = self.userCheckRate
                appsecs -= 1
                if appsecs <= 0:
                    self.sendAppList()
                    self.sendInfo()
                    appsecs = self.appRefreshRate
                time.sleep(1)
            logging.debug("AgentLogicBase:: doWork() exiting")
        except:
            logging.error("AgentLogicBase::doWork - '%s'"%(traceback.format_exc()))

    def doListen(self):
        logging.debug("AgentLogicBase::doListen() - entered")
        if self.commandHandler == None:
            logging.debug("AgentLogicBase::doListen() - no commandHandler ... exiting doListen thread")
            return
        while self.cont_flag:
            try:
                logging.debug("AgentLogicBase::doListen() - in loop before hcRecv")
                msg = self.hc.hcRecv()
                unPackedMessage = self.hc.unpackMsg(msg)
                if len(unPackedMessage) == 0:
                    raise Exception("AgentLogicBase::doListen() - could not unpack message '%s'"%(msg))
                logging.debug("AgentLogicBase::doListen() - recieved msg ='%s'"%(unPackedMessage))
                self.commandHandler.handleCommand(unPackedMessage[3])
            except:
                logging.error("AgentLogicBase::doListen - '%s'"%(traceback.format_exc()))
                time.sleep(1)
        logging.debug("AgentLogicBase::doListen() - exiting")


    def sendUserInfo(self, force=False):
        cur_user = str(self.dr.getActiveUser())
        logging.debug("AgentLogicBase::sendUserInfo - cur_user = '%s'"%(cur_user))
        if cur_user != self.activeUser or force:
            self.hc.hcSend(messageType.userInfo, cur_user)
            self.activeUser = cur_user

    def sendInfo(self):
        self.hc.hcSend(messageType.machineName, self.dr.getMachineName())
        self.hc.hcSend(messageType.guestOs, self.dr.getOsVersion())
        self.hc.hcSend(messageType.IPAddresses, self.dr.getAllNetworkInterfaces())

    def sendAppList(self):
        if self.hc.hcSend(messageType.flushApps, "") == 0:
            app_list = self.dr.getApplications()
            for app in app_list:
                if self.hc.hcSend(messageType.newApp, app) != 0:
                    logging.error("AgentLogicBase::sendAppList - faied sending 'newApp' (%s) message"%(app))
        else:
            logging.error("AgentLogicBase::sendAppList - failed sending 'flushApps' msg")

